#region License

/* Copyright (c) 2008, Dusty Candland
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 * * Neither the name of Dusty Candland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#endregion

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data.SQLite;
using System.IO;

namespace NS3Library.SQLite
{
    public class SQLiteS3Service : IS3Service
    {
        private readonly BucketRepository _bucketRepo;
        private readonly ObjectRepository _objectRepo;
        private readonly MetaDataRepository _metaDataRepo;

        public SQLiteS3Service(string fileName)
        {
            string connectionString = "Data Source=" + fileName;
            if (!File.Exists(fileName))
                SQLiteConnection.CreateFile(fileName);

            _bucketRepo = new BucketRepository(connectionString);
            _bucketRepo.EnsureBucketsTable();

            _objectRepo = new ObjectRepository(connectionString);
            _objectRepo.EnsureObjectsTable();

            _metaDataRepo = new MetaDataRepository(connectionString);
            _metaDataRepo.EnsureMetaDataTable();
        }

        public IS3Bucket Put(IS3Bucket s3Bucket)
        {
            int id = _bucketRepo.GetBucketId(s3Bucket.Name);
            if (id == 0)
            {
                _bucketRepo.Insert(s3Bucket);
                id = _bucketRepo.GetBucketId(s3Bucket.Name);
            }
            else
            {
                _bucketRepo.Update(id, s3Bucket);
            }
            return _bucketRepo.Find(id);
        }

        public IS3Object Put(IS3Object s3Object)
        {
            int bucketId = EnsureS3BucketIsSaved(s3Object.BucketName);

            if (Get(s3Object.BucketName, s3Object.Key) == null)
            {
                _objectRepo.Insert(bucketId, s3Object);
            }
            else
            {
                _objectRepo.Update(bucketId, s3Object);
            }

            IS3Object savedObject = Get(s3Object.BucketName, s3Object.Key);
            _metaDataRepo.Save(((SQLiteS3Object) savedObject).Id, s3Object.Metadata);
            return savedObject;
        }

        public IEnumerable<IS3Bucket> Get()
        {
            return _bucketRepo.FindAll();
        }


        public IS3Object Get(string bucketName, string key)
        {
            NameValueCollection meta = _metaDataRepo.Get(_objectRepo.GetObjectId(bucketName, key));
            return _objectRepo.Find(bucketName, key, meta);
        }

        public IS3Object Get(IS3Bucket bucket, string key)
        {
            return Get(bucket.Name, key);
        }

        public IS3Bucket Get(string bucketName)
        {
            return _bucketRepo.Find(bucketName);
        }

        public IS3Bucket Get(string bucket, string prefix, int maxKeys, string marker)
        {
            throw new NotImplementedException();
        }

        public IS3Bucket Get(string bucket, string prefix, int maxKeys, string marker, string delimiter)
        {
            throw new NotImplementedException();
        }

        public void DeleteBucket(IS3Bucket s3Bucket)
        {
            DeleteBucket(s3Bucket.Name);
        }

        public void DeleteBucket(string bucketName)
        {
            _bucketRepo.Delete(bucketName);
        }

        public void DeleteObject(IS3Object s3Object)
        {
            DeleteObject(s3Object.BucketName, s3Object.Key);
        }

        public void DeleteObject(string bucketName, string key)
        {
            _metaDataRepo.Delete(_objectRepo.GetObjectId(bucketName, key));
            _objectRepo.Delete(bucketName, key);
        }

        private int EnsureS3BucketIsSaved(string bucketName)
        {
            if (!_bucketRepo.Exists(bucketName))
                return ((SQLiteS3Bucket) Put(new SQLiteS3Bucket(bucketName))).Id;
            return _bucketRepo.GetBucketId(bucketName);
        }

        public IS3Bucket CreateIS3Bucket(string name, S3LocationConstraint locationConstraint, S3Acl acl)
        {
            return new SQLiteS3Bucket(name, locationConstraint, acl);
        }

        public IS3Bucket CreateIS3Bucket(string name)
        {
            return new SQLiteS3Bucket(name);
        }

        public IS3Object CreateIS3Object(string bucketName, string key, byte[] content, NameValueCollection metadata)
        {
            return new SQLiteS3Object(bucketName, key, content, metadata);
        }
    }
}